 
/* 
 * Copyright (C) Ignacio Lucas
 */
 
/*
 *  Implementacion simplificada del protocolo HTTP.
 *
 *  Ignacio Lucas <ignacio.lucas@gmail.com>
 *
 */
#include "http.h"
#include "http_parser.h"
#include "../worker.h" // usado para el object pool.
#include "../user_pools.h"

// Tamanio maximo de elemento en http header. Usado por incremental buffer.
#define MAX_ELEMENT_SIZE 2048

// buffer usado para leer bytes del socket.
#define SOCK_BUF_SIZE 2048

// tipos de objetos: request o response.
enum message_type { REQUEST, RESPONSE };

// buffer para ir agregando bytes de forma incremental.
typedef struct s_incremental_buffer
{
	char buf[MAX_ELEMENT_SIZE];
	int len;
	int dummy_allign;	// 4byte and 8 byte alligned.

} incremental_buffer;

// campos comunes de un request y un response.
typedef struct s_http_common_fields
{
	enum http_method method;  // metodo HTTP
	unsigned short http_major; // major version HTTP 
  unsigned short http_minor; // minor version HTTP
	int content_length; // longitud del cuerpo
	int status_code; // codigo de estado del request/response
	hashmap_t headers; //encabezados.
	char *body;  // cuerpo
	int already_allocated_body_length;  // bytes alocados para resguardar el cuerpo (body)
	int current_body_length; // longitud actual del cuerpo (body)
	incremental_buffer last_header_name; // ultimo nombre de encabezado parseado.
	incremental_buffer last_header_value; // ultimo valor de encabezado parseado.

// funciones de callback del modulo http_parser.
	int message_begin_cb_called;
  int headers_complete_cb_called;
  int message_complete_cb_called;
  int message_complete_on_eof;
  
  // socket asociado al request / response.
  socket_t socket;
  
} http_common_fields;

// estructura de un response.
typedef struct s_http_response
{
	http_common_fields common;  // campos comunes.
	http_request_t request; // request asociado al response.
	
} http_response;

// estructura de un request.
typedef struct s_http_request
{
	http_common_fields common;  // campos comunes.
	hashmap_t parameters;  // parametros del request.
	incremental_buffer request_path;  // path solicitado.
  	incremental_buffer request_url; // url completa solicitada.
  	incremental_buffer query_string; // querystring (lo que viene detras del signo de pregunta '?')
	http_response_t response; // response asociada al request.
	int dummy_allign;	// 4byte and 8 byte alligned.
	
} http_request;

// union para manejo unificado de request y response.
typedef union u_http_all_type_structs
{
	http_response *response;
	http_request *request;
	
} http_all_type_structs;

// helper para parseo de request y response.
typedef struct s_http_message_helper
{
	enum message_type type;
	http_all_type_structs ref;
	enum { NONE=0, FIELD, VALUE } last_header_element;
} http_message_helper;


// vector de descripcion de los codigos de estado HTTP mas comunes.
#define HTTP_REASONS_BASE_100	0
#define HTTP_REASONS_BASE_200	2
#define HTTP_REASONS_BASE_300	9
#define HTTP_REASONS_BASE_400	15
#define HTTP_REASONS_BASE_500	31


static char http_reasons[][40] = {
	"Continue", // 100
	"Switching Protocols", //101
	"OK", // 200
	"Created", //201
	"Accepted", //202
	"Non-Authoritative Information", //203
	"No Content", //204
	"Reset Content", //205
	"Partial Content", //206
	"Multiple Choices", //300
	"Moved Permanently", //301
	"Moved Temporarily", //302
	"See Other", //303
	"Not Modified", //304
	"Use Proxy", //305
	"Bad Request", //400
	"Unauthorized", //401
	"Payment Required", //402
	"Forbidden", //403
	"Not Found", //404
	"Method Not Allowed", //405
	"Not Acceptable", //406
	"Proxy Authentication Required", //407
	"Request Time-out", //408
	"Conflict", //409
	"Gone", //410
	"Length Required", //411
	"Precondition Failed", //412
	"Request Entity Too Large", //413
	"Request-URI Too Large", //414
	"Unsupported Media Type", //415
	"Internal Server Error", //500
	"Not Implemented", //501
	"Bad Gateway", //502
	"Service Unavailable", //503
	"Gateway Time-out", //504
	"HTTP Version not supported" //505
};


// macros para el parseo y resguardo de valores sobre los request y response.

#define GET_DATA_FROM_PARSER(p)	(((http_message_helper *)(((http_parser *)p)->data)))

#define GET_COMMON_FIELD_REF(p, field)	 ((GET_DATA_FROM_PARSER(p)->type == (enum message_type)REQUEST)?&((((GET_DATA_FROM_PARSER(p)->ref).request)->common).field):&((((GET_DATA_FROM_PARSER(p)->ref).response)->common).field))
	
#define GET_COMMON_FIELD_REF_DIRECT(obj,field)	(&((obj->common).field))

#define RESET_INCREMENTAL_BUFFER(ib)	\
do {                                  \
	ib->len=0;													\
} while (0)

#define SET_INCREMENTAL_BUFFER(ib, str, morelen, ret) ({ if (ib->len + morelen >= MAX_ELEMENT_SIZE) {*ret=1;} else {if (morelen > 0) {strncpy(ib->buf+ib->len, str, morelen);ib->len+=morelen; if (ib->len < MAX_ELEMENT_SIZE){ib->buf[ib->len]='\0';}}	*ret=0;}})
	
#define STR_IN_RANGE(s, from, len)	(( (s != NULL) && (s < (from + len))) ? 1 : 0)

#define SEND_ON_SOCKET(sock, buf, len, sockret, sockok)	\
do {                                  							\
	*sockok=1;																				\
	*sockret=socket_write(sock, buf, len);						\
	*sockok = (*sockret == len)?1:0;									\
} while (0)

#define ADD_OR_SERIALIZE(ib, str, morelen, sock, ret, sockret, sockok)	\
do {                                  																	\
	*sockok=1;																														\
	SET_INCREMENTAL_BUFFER(ib, str, morelen, ret); 												\
	if (*ret == 1){ /* full buffer */							 												\
		SEND_ON_SOCKET(sock, ib->buf, ib->len, sockret, sockok);            \
		if (*sockok == 1)																										\
		{																																		\
			RESET_INCREMENTAL_BUFFER(ib);																			\
			SET_INCREMENTAL_BUFFER(ib, str,morelen, ret);											\
		}																																		\
	}																							 												\
} while (0)

#define ADD_OR_SERIALIZE_HASHMAP(ib, hash, iter, key, val, separator, lensep, endsep, endlen, sock, ret, sockret, sockok)			\
do {                                  																																\
	while ((!hashmap_is_end(hash, iter)) && (*sockok == 1)){			 																			\
		hashmap_return_entry(hash, iter, key, (void **)val);																							\
		ADD_OR_SERIALIZE(ib, *key, strlen(*key), sock, (ret), (sockret), (sockok));												\
		ADD_OR_SERIALIZE(ib, separator, lensep, sock, (ret), (sockret), (sockok));												\
		ADD_OR_SERIALIZE(ib, *val, ((*val == NULL)?0:strlen(*val)), sock, (ret), (sockret), (sockok));												\
		iter++;																																														\
		if (!hashmap_is_end(hash, iter)){																																	\
			ADD_OR_SERIALIZE(ib, endsep, endlen, sock, (ret), (sockret), (sockok));													\
		}																																																	\
	}																							 																											\
} while (0)

static void* http_parse_request_or_response(enum message_type type, socket_t socket);	

               
int http_destroy()
{
	return 0;	
}

int http_init()
{
	return 0;
}

// utilidades para incluir los socket buffers en el object pool
size_t http_get_socket_buffer_size()
{
	return SOCK_BUF_SIZE;
}

// permite obtener espacio de memoria para asignar a un socket. Tomado del oject_pool asociado.
static char *http_get_socket_buffer()
{
	return (char *)user_pools_get("HTTP_SOCKET_BUFFER_POOL");
}

// devuelve un socket al object_pool.
static void http_put_socket_buffer(char *buf)
{
	user_pools_put("HTTP_SOCKET_BUFFER_POOL", buf);
}


// utilidades para incluir a incremental_buffer en el object pool
size_t http_get_incremental_buffer_size()
{
	return sizeof(incremental_buffer);
}

// permite obtener un incremental_buffer del object_pool asociado.
static incremental_buffer * http_get_incremental_buffer()
{
	return (incremental_buffer *)user_pools_get("HTTP_INCREMENTAL_BUFFER_POOL");
}

// devuelve un incremental_buffer al object_pool asociado.
static void http_put_incremental_buffer(incremental_buffer *buf)
{
	user_pools_put("HTTP_INCREMENTAL_BUFFER_POOL", buf);
}

// utilidades para incluir a http_request en el object pool
size_t http_get_request_size()
{
	return sizeof(http_request);
}

// permite obtener memoria para asignar a un request. Tomado del object_pool asociado.
static http_request * http_get_http_request()
{
	return (http_request *)user_pools_get("HTTP_REQUEST_POOL");
}

// devuelve un request al object_pool asociado.
static void http_put_http_request(http_request *request)
{
	user_pools_put("HTTP_REQUEST_POOL", request);
}

// utilidades para incluir a http_response en el object pool
size_t http_get_response_size()
{
	return sizeof(http_response);
}

// permite obtener memoria para asignar a un response. Tomado del object_pool asociado.
static http_response * http_get_http_response()
{
	return (http_response *)user_pools_get("HTTP_RESPONSE_POOL");
}

// devuelve un response al object_pool asociado.
static void http_put_http_response(http_response *response)
{
	user_pools_put("HTTP_RESPONSE_POOL", response);
}



// asigna en "reason" la descipcion asociado a un codigo de estado HTTP.
static int http_get_reason(int status_code, const char **reason)
{
	// 100*(int)(status_code / 100)
	// *reason=(const char *)(http_reasons[status_code - 100*(int)(status_code / 100) + http_reasons[(int)(status_code / 100) -1]);

	// suponiendo 20X  es el caso mas comun, lo ponemos primero.
	if (status_code >= 200 && status_code < 300)
	{
		*reason=(const char *)(http_reasons[status_code - 200 + HTTP_REASONS_BASE_200]);
		return 0;
	}
	if (status_code >= 400 && status_code < 500)
	{
		*reason=(const char *)(http_reasons[status_code - 400 + HTTP_REASONS_BASE_400]);
		return 0;
	}
	if (status_code >= 300 && status_code < 400)
	{
		*reason=(const char *)(http_reasons[status_code - 300 + HTTP_REASONS_BASE_300]);
		return 0;
	}
	if (status_code >= 500 && status_code < 600)
	{
		*reason=(const char *)(http_reasons[status_code - 500 + HTTP_REASONS_BASE_500]);
		return 0;
	}
	if (status_code >= 100 && status_code < 200)
	{
		*reason=(const char *)(http_reasons[status_code - 100 + HTTP_REASONS_BASE_100]);
		return 0;
	}
	return 1;
}

// inicializa los campos comunes de un request/response.
static int http_initialize_common_fields(http_common_fields *cf)
{
	cf->content_length = 0;
	cf->status_code = 0;
	cf->body = NULL;
	cf->already_allocated_body_length = 0;
	cf->current_body_length = 0;
	cf->headers = hashmap_create(3);
	
	RESET_INCREMENTAL_BUFFER((&(cf->last_header_name)));
	RESET_INCREMENTAL_BUFFER((&(cf->last_header_value)));
	cf->socket = NULL;
	return 0;
}

// callback al comenzar el parseo de un mensaje.
static int http_message_begin_cb (http_parser *p)
{
	*GET_COMMON_FIELD_REF(p, message_begin_cb_called) = 1;
  return 0;
}

// asigna el ultimo encabezado parseado (una vez que se tiene su nombre y valor)
static int http_load_last_header_field_and_value(http_parser *p)
{
	int ret=0;
	incremental_buffer *lhn = GET_COMMON_FIELD_REF(p, last_header_name);
	incremental_buffer *lhv = GET_COMMON_FIELD_REF(p, last_header_value);
	
	if ((lhn->len > 0) && (lhv->len > 0))
	{
		ret = hashmap_put(*GET_COMMON_FIELD_REF(p, headers), lhn->buf, lhv->buf);
	}
	return ret;
}

// callback al encontrar el comienzo de un encabezado.
static int http_header_field_cb (http_parser *p, const char *buf, size_t len)
{
	incremental_buffer *lhn = GET_COMMON_FIELD_REF(p, last_header_name);
	http_message_helper *helper = (http_message_helper *)p->data;
	int ret=0;
	
	if (helper->last_header_element == VALUE)
	{
		http_load_last_header_field_and_value(p);
		RESET_INCREMENTAL_BUFFER(lhn);
	}
	helper->last_header_element = FIELD;
	SET_INCREMENTAL_BUFFER(lhn, buf, len, (&ret));
	return ret;
}

// callback al encontrar el valor de un encabezado.
static int http_header_value_cb (http_parser *p, const char *buf, size_t len)
{
	int ret=0;
	incremental_buffer *lhv = GET_COMMON_FIELD_REF(p, last_header_value);
	http_message_helper *helper = (http_message_helper *)p->data;
	
	if (helper->last_header_element == FIELD)
	{
		RESET_INCREMENTAL_BUFFER(lhv);
	}
	helper->last_header_element = VALUE;
	SET_INCREMENTAL_BUFFER(lhv, buf, len, (&ret));
	return ret;
}

// callback al encontrar el path requerido.
static int http_request_path_cb (http_parser *p, const char *buf, size_t len)
{
	int ret=0;
	incremental_buffer *request_path = &( ((GET_DATA_FROM_PARSER(p))->ref).request->request_path);
	SET_INCREMENTAL_BUFFER(request_path, buf, len, (&ret));
	return ret;
}

// callback al encontrar la url requerida.
static int http_request_url_cb (http_parser *p, const char *buf, size_t len)
{
	int ret=0;
	incremental_buffer *request_url = &(GET_DATA_FROM_PARSER(p)->ref.request->request_url);
	SET_INCREMENTAL_BUFFER(request_url, buf, len, (&ret));
	return ret;
}

// callback al encontrar un fragmento (no utilizado).
static int http_fragment_cb (http_parser *p, const char *buf, size_t len)
{
  return 0;
}

// callback al encontrar el querystring (request por metodo GET solamente).
static int http_querystring_cb(http_parser *p, const char *buf, size_t len)
{
	int ret=0;
	incremental_buffer *query_string = &(GET_DATA_FROM_PARSER(p)->ref.request->query_string);
	SET_INCREMENTAL_BUFFER(query_string, buf, len, (&ret));
	return ret;	
}

// parseo del querystring.
// asumo querystring de la forma key=value&key2=value2 (value siempre presente)
// Sino, el parser cancela.
static int http_load_parameters(http_parser *p, char *buf, int len)
{
	incremental_buffer *key;
	incremental_buffer *val;

	char *token_index, *key_index, *val_index, *token_index_2;
	int wrong_querystring=0, end_of_querystring=0;
	int ret=0;

	if (len == 0)
	{
		return 0;
	}
	
	// Parseo cada parametro del request usando incremental buffers.
	if ((key = http_get_incremental_buffer()) == NULL)
	{
		return 1;
	}
	if ((val = http_get_incremental_buffer()) == NULL)
	{
		http_put_incremental_buffer(key);
		return 1;
	}
	
	// Posiciono en el comienzo del query_string buffer.
	token_index = buf;
	
	while ((!wrong_querystring) && (!end_of_querystring))
	{
		RESET_INCREMENTAL_BUFFER(key);
		RESET_INCREMENTAL_BUFFER(val);

		key_index=val_index=NULL;
		key_index=token_index;
		token_index_2=strstr(key_index, "=");
		
		if (STR_IN_RANGE(token_index_2, buf, len))
		{
			val_index=token_index_2+1;
			if (STR_IN_RANGE(val_index, buf, len))
			{
				// Extraigo key.

				SET_INCREMENTAL_BUFFER(key, key_index, (token_index_2 - key_index), &(ret));

				token_index=strstr(val_index,"&");
				if (token_index != NULL)
				{
					// Extraigo value.

					SET_INCREMENTAL_BUFFER(val, val_index, (token_index - val_index), &(ret));


					token_index++;
					if (!STR_IN_RANGE(token_index, buf, len))
					{
						end_of_querystring=1;
					}
				}
				else
				{
					SET_INCREMENTAL_BUFFER(val, val_index, (buf + len-val_index), &(ret));
					end_of_querystring=1;
				}
			}
			else
			{
				// Extraigo key hasta el ultimo caracter de buf
				SET_INCREMENTAL_BUFFER(key, key_index, (buf + len - key_index), &(ret));
			}
			
			if (key->len > 0)
			{
				// Agrego el nuevo parametro al hashmap.
				hashmap_put( (GET_DATA_FROM_PARSER(p)->ref.request->parameters), (const char *)(key->buf), (const char *)(val->buf));
			}
		}
		else
		{
			wrong_querystring=1;
		}		
	}
	http_put_incremental_buffer(key);
	http_put_incremental_buffer(val);

	return wrong_querystring;
}

// callback al encontrar el body.
static int http_body_cb (http_parser *p, const char *buf, size_t len)
{
	char **body = GET_COMMON_FIELD_REF(p, body);
	
	// Chequeo si content-length esta seteado. En ese caso, reservo memoria para guardar el body.
	int *content_length = GET_COMMON_FIELD_REF(p, content_length);
	int *already_allocated_body_length = GET_COMMON_FIELD_REF(p, already_allocated_body_length);
	int *current_body_length = GET_COMMON_FIELD_REF(p, current_body_length);;
	
	if ((*content_length) > 0)
	{
		if (*body == NULL)
		{
			// byte extra para poner '\0' al final.
			*body = malloc((*content_length)+1);
		}
	}
	else
	{
		// realloc para el body
		// byte extra para poner '\0' al final.
		*body = realloc(*body, (*already_allocated_body_length) + len + 1);
		*(already_allocated_body_length)+= len;
	}
	if (*body == NULL) return 1;
	
	// aseguramos tener el espacio de memoria suficiente para backupear el body.
	if (((*content_length) > 0) && ( ( (*current_body_length) + len) > (*content_length)))
	{
		return 2;
	}
	memcpy((*body) + (*current_body_length), buf, len);
	*current_body_length += len;
	
	(*body)[(*current_body_length)]='\0';
	return 0;
}

// callback al haber finalizado el parseo de todos los encabezados.
static int http_headers_complete_cb (http_parser *parser)
{
	http_load_last_header_field_and_value(parser);
	
	*GET_COMMON_FIELD_REF(parser, method) = parser->method;
  *GET_COMMON_FIELD_REF(parser, status_code) = parser->status_code;
  *GET_COMMON_FIELD_REF(parser, http_major) = parser->http_major;
  *GET_COMMON_FIELD_REF(parser, http_minor) = parser->http_minor;
  *GET_COMMON_FIELD_REF(parser, headers_complete_cb_called) = 1;
  
  if (parser->content_length > 0)
  {
  	*GET_COMMON_FIELD_REF(parser, content_length) = parser->content_length;
  }
  
  return 0;
}

// ajusta el content-length en caso de encontrar diferencias respecto al encabezado "content-length" informado.
// el campo content-length pudo no haber sido informado.
static int http_adjust_content_length(http_parser *p)
{
	int *content_length = GET_COMMON_FIELD_REF(p, content_length);
	int *current_body_length = GET_COMMON_FIELD_REF(p, current_body_length);;
	
	if ( ((*content_length) == 0) && ((*current_body_length) > 0))
	{
		*content_length = *current_body_length;
		return 0;
	}
	
	// confirmamos que el largo del body sea el que nos informaron.
	if ( ((*content_length) > 0) && ((*current_body_length) != (*content_length)))
	{
		return 1;
	}
	return 0;	
}

// callback al haber finalizado el parseo de todo el request/response.
static int http_message_complete_cb (http_parser *parser)
{
	int ret=0;
	incremental_buffer *query_string = NULL;
	char *content_type = NULL;
	hashmap_t headers;

	
	ret=http_adjust_content_length(parser);
	if (ret != 0)
	{
		return ret;
	}
	// SI metodo GET, cargamos querystring.
	// SI metodo POST y Content-Type == application/x-www-form-urlencoded
	if (GET_DATA_FROM_PARSER(parser)->type == REQUEST)
	{
		if (*GET_COMMON_FIELD_REF(parser, method) == (enum http_method) HTTP_GET)
		{
			query_string = &(GET_DATA_FROM_PARSER(parser)->ref.request->query_string);
			ret=http_load_parameters(parser, query_string->buf, query_string->len);
		}
		if (*GET_COMMON_FIELD_REF(parser, method) == (enum http_method) HTTP_POST)
		{
			headers=*GET_COMMON_FIELD_REF(parser, headers);
			if ((hashmap_entry_by_key(headers, "Content-Type", (void **)(&content_type)) > 0) && 
					((strcasecmp(content_type, "application/x-www-form-urlencoded") == 0)))
			{
				ret=http_load_parameters(parser, *GET_COMMON_FIELD_REF(parser, body), *GET_COMMON_FIELD_REF(parser, current_body_length));
			}
		}
		if (ret != 0)
		{
			return ret;
		}
	}
	*GET_COMMON_FIELD_REF(parser, message_complete_cb_called) = 1;
	return 0;
}

// inicializacion de todos los campos de un request.
static int http_request_initialize_fields (http_request *request)
{
	http_initialize_common_fields(&(request->common));
		
	RESET_INCREMENTAL_BUFFER((&(request->request_path)));
	RESET_INCREMENTAL_BUFFER((&(request->request_url)));
	RESET_INCREMENTAL_BUFFER((&(request->query_string)));
	request->parameters = hashmap_create(3);
	return 0;
}

// inicializacion de todos los campos de un response.
static int http_response_initialize_fields (http_response *response)
{
	http_initialize_common_fields(&(response->common));
	return 0;
}

// funcion generica para parsear un request/response a partir de la lectura de un socket.
static void* http_parse_request_or_response(enum message_type type, socket_t socket)
{
	http_parser parser;
	http_message_helper helper;
	char *buf;
	int leido=0, parser_ret = 0;
	int socket_err = 0, parser_err = 0;
	void *ret=NULL;
	
	buf = http_get_socket_buffer();
	if (buf == NULL)
	{
		return NULL;
	}
	
	http_parser_init(&parser);
	helper.type = type;
	helper.last_header_element = NONE;
	
	if (type == REQUEST)
	{
		helper.ref.request = http_get_http_request();
		if (helper.ref.request == NULL)
		{
			http_put_socket_buffer(buf);
			return NULL;
		}
		ret = (void *)helper.ref.request;
		http_request_initialize_fields(helper.ref.request);
		helper.ref.request->common.socket = socket;
	}
	else
	{
		helper.ref.response = http_get_http_response();

		if (helper.ref.response == NULL)
		{
			http_put_socket_buffer(buf);
			return NULL;
		}
		ret = (void *)helper.ref.response;
		http_response_initialize_fields(helper.ref.response);
		helper.ref.response->common.socket = socket;
	}
	parser.data = &helper;
	
	// Inicializo los flags a 0
	
	*GET_COMMON_FIELD_REF(&parser, message_begin_cb_called) = 0;
	*GET_COMMON_FIELD_REF(&parser, headers_complete_cb_called) = 0;
	*GET_COMMON_FIELD_REF(&parser, message_complete_cb_called) = 0;
	*GET_COMMON_FIELD_REF(&parser, message_complete_on_eof) = 0;
	
	
	parser.on_message_begin     = http_message_begin_cb;
  parser.on_header_field      = http_header_field_cb;
  parser.on_header_value      = http_header_value_cb;
  parser.on_path              = http_request_path_cb;
  parser.on_url               = http_request_url_cb;
  parser.on_fragment          = http_fragment_cb;
  parser.on_query_string      = http_querystring_cb;
  parser.on_body              = http_body_cb;
  parser.on_headers_complete  = http_headers_complete_cb;
  parser.on_message_complete  = http_message_complete_cb;
  
  
  // Leo del socket hasta que se ejecute message_complete_cb_called o detecte un error.
  while (( ! *GET_COMMON_FIELD_REF(&parser, message_complete_cb_called)) && (parser_err == 0) && (socket_err == 0))
	{
		leido = socket_read(socket, buf, SOCK_BUF_SIZE);
		if (leido > 0)
		{
			parser_ret=((type == REQUEST)?http_parse_requests (&parser, buf, leido):http_parse_responses (&parser, buf, leido));
			parser_err = (! (parser_ret == leido));
		}
		else
		{
			socket_err=1;
			if (leido == 0)
			{
				// cliente cerro la conexion!
			}
		}
	}
	http_put_socket_buffer(buf);

	// Verificamos la razon por la que se salio del while
	if (socket_err || parser_err)
	{
		if (type == REQUEST)
		{
			http_put_http_request(helper.ref.request);
		}
		else
		{
			http_put_http_response(helper.ref.response);
		}
		return NULL;
	}
	return ret;
}

http_request_t http_request_parse(socket_t socket)
{
	http_request_t request;
	request = (http_request_t)http_parse_request_or_response(REQUEST, socket);
	return request;
}

http_response_t http_response_parse(socket_t socket)
{
	http_response_t response;
	response = (http_response_t)http_parse_request_or_response(RESPONSE, socket);
	return response;
}

hashmap_t http_response_get_headers(http_response_t response)
{
	return *GET_COMMON_FIELD_REF_DIRECT(response, headers);
}

hashmap_t http_request_get_headers(http_request_t request)
{
	return *GET_COMMON_FIELD_REF_DIRECT(request, headers);
}

int http_request_get_header(http_request_t request, const char *key, char **value)
{
	hashmap_t h = http_request_get_headers(request);
	ssize_t len = hashmap_entry_by_key(h, key, (void **)value);
	if (len > 0) return 0;
	return 1;
}

char * http_request_get_request_path(http_request_t request)
{
	return ((http_request *)request)->request_path.buf;
}

char * http_request_get_request_url(http_request_t request)
{
	return ((http_request *)request)->request_url.buf;
}


int http_response_get_header(http_response_t response, const char *key, char **value)
{
	hashmap_t h = http_response_get_headers(response);
	ssize_t len = hashmap_entry_by_key(h, key, (void **)value);
	if (len > 0) return 0;
	return 1;
}

int http_request_add_header(http_request_t request, const char *key, const char *val)
{
	return hashmap_put(*GET_COMMON_FIELD_REF_DIRECT(request, headers), key, val);
}

// keepref = 1 entonces el parametro "body" debemos liberarlo luego de ser usado (para evitar hacer una copia).
static int http_set_body(http_common_fields *cf, const char *body, int body_len, int keepref)
{
	char newlen[50];
	char *newbody = NULL;
	
	memset(newlen, 0, sizeof(newlen));
	
	if (body_len > 0)
	{
		if (keepref)
		{
			newbody=(char *)body;
		}
		else
		{
			// intentamos alocar memoria para backupear a body
			newbody = malloc(sizeof(char)*(body_len + 1));
			if (newbody == NULL)
			{
				return 1;
			}
			newbody[body_len]='\0';
			memcpy(newbody, body, body_len);
		}
	}
	sprintf(newlen, "%d", body_len);
	
	// liberamos espacio de memoria alocado previamente.
	if ((cf->body != NULL) && (cf->current_body_length > 0))
	{
		free(cf->body);
		cf->body=NULL;
	}
	cf->body = newbody;
	cf->current_body_length = body_len;
	
	// definimos el valor de content-length (borramos el valor viejo - si hubiera -)
	hashmap_remove(cf->headers, "Content-Length");
	hashmap_put(cf->headers, "Content-Length", (const char *)newlen);
	return 0;	
}

int http_request_set_body(http_request_t request, const char *body, int body_len, int keepref)
{
	return http_set_body(&(((http_request *)request)->common), body, body_len, keepref);
}

int http_response_set_status_code(http_response_t response, int status_code)
{
	*GET_COMMON_FIELD_REF_DIRECT(response,status_code)=status_code;
	return 0;
}

int http_response_get_status_code(http_response_t response)
{
	return *GET_COMMON_FIELD_REF_DIRECT(response,status_code);
}

int http_response_set_version(http_response_t response, unsigned short http_major, unsigned short http_minor)
{
	*GET_COMMON_FIELD_REF_DIRECT(response,http_major)=http_major;
	*GET_COMMON_FIELD_REF_DIRECT(response,http_minor)=http_minor;
	return 0;
}

int http_response_set_body(http_response_t response, const char *body, int body_len, int keepref)
{
	return http_set_body(&(((http_response *)response)->common), body, body_len, keepref);
}

int http_request_get_body(http_request_t request, const char **body, int *body_len)
{
	*body=*GET_COMMON_FIELD_REF_DIRECT(request,body);
	*body_len=*GET_COMMON_FIELD_REF_DIRECT(request,current_body_length);
	return 0;
}

int http_response_get_body(http_response_t response, const char **body, int *body_len)
{
	*body=*GET_COMMON_FIELD_REF_DIRECT(response,body);
	*body_len=*GET_COMMON_FIELD_REF_DIRECT(response,current_body_length);
	return 0;
}

int http_response_add_header(http_response_t response, const char *key, const char *val)
{
	return hashmap_put(*GET_COMMON_FIELD_REF_DIRECT(response, headers), key, val);
}

int http_request_remove_header(http_request_t request, const char *key)
{
	return hashmap_remove(*GET_COMMON_FIELD_REF_DIRECT(request, headers), key);
}

hashmap_t http_request_get_parameters(http_request_t request)
{
	return ((http_request *)request)->parameters;
}

char* http_request_get_parameter(http_request_t request, const char *key)
{
	char *val = NULL;
	//ssize_t ret;
	hashmap_t hash = http_request_get_parameters(request);
	hashmap_entry_by_key(hash, key, (void **)(&val));
	return val;
}

int http_request_add_parameter(http_request_t request, const char *key, const char *val)
{
	return hashmap_put(((http_request *)request)->parameters, key, val);
}


// destruye los campos comunes de un request/response.
static int http_destroy_common_fields(http_common_fields * cm)
{
	if (cm->body != NULL)
	{
		free(cm->body);
		cm->body=NULL;
	}
	if (cm->headers != NULL)
	{
		hashmap_delete(cm->headers);
		cm->headers=NULL;
	}
  cm->socket=NULL;
  return 0;
}

int http_request_destroy(http_request_t req, int is_socket_destroy)
{
	http_request *request = (http_request *)req;
	socket_t sock = NULL;
	
	sock = *GET_COMMON_FIELD_REF_DIRECT(request, socket);
	
	if ((sock != NULL) && (is_socket_destroy))
	{
		socket_destroy(sock);
	}
		
	http_destroy_common_fields(&(request->common));
	if (request->parameters != NULL)
	{
		hashmap_delete(request->parameters);
		request->parameters=NULL;
	}	
	
	// devolucion al object pool.
	http_put_http_request(request);
	return 0;
}

http_request_t http_request_create(
						enum http_method method,
					 	unsigned short http_major,
					 	unsigned short http_minor,
					 	char *request_path)
{
	int ret;

	// acceso al object pool.
	http_request *request = http_get_http_request();

	if (request == NULL) return NULL;
	
	http_request_initialize_fields(request);	
	
	*GET_COMMON_FIELD_REF_DIRECT(request, method) = method;
	*GET_COMMON_FIELD_REF_DIRECT(request, http_major) = http_major;
	*GET_COMMON_FIELD_REF_DIRECT(request, http_minor) = http_minor;
	
	SET_INCREMENTAL_BUFFER((&(request->request_path)), request_path, strlen(request_path), (&ret));
	
	// La longitud de request_path excede el maximo.
	if (ret == 1)
	{
		http_request_destroy(request, 0);
		return NULL;
	}
	return request;
}

int http_request_set_socket(http_request_t request, socket_t socket)
{
	*GET_COMMON_FIELD_REF_DIRECT(request, socket) = socket;
	return 0;	
}

socket_t http_request_get_socket(http_request_t request)
{
	return *GET_COMMON_FIELD_REF_DIRECT(request, socket);
}

int http_response_set_socket(http_response_t response, socket_t socket)
{
	*GET_COMMON_FIELD_REF_DIRECT(response, socket) = socket;
	return 0;	
}

socket_t http_response_get_socket(http_response_t response)
{
	return *GET_COMMON_FIELD_REF_DIRECT(response, socket);
}

http_response_t http_response_create(http_request_t req)
{
	// acceso al object pool
	http_response *response = http_get_http_response();

	http_request *request = (http_request *)req;
	if (response == NULL) return NULL;

	http_initialize_common_fields(&(response->common));	
	response->request = request;
	
	http_response_set_version(response, request->common.http_major, request->common.http_minor);
	return response;
}

int http_response_destroy(http_response_t response, int is_socket_destroy)
{
	socket_t socket = *GET_COMMON_FIELD_REF_DIRECT(response, socket);
	if ((socket != NULL) && (is_socket_destroy))
	{
		socket_destroy(socket);
	}
	http_destroy_common_fields(&(((http_response *)response)->common));

	// devolucion al object pool.
	http_put_http_response(response);
	return 0;
}

int http_request_serialize_and_send_on_socket(http_request_t req, socket_t socket)
{
	incremental_buffer *buf = NULL;
	int ret=0, sockret = 0, sockok = 1;
	http_request *request = (http_request *)req;
	enum http_method http_m;
	char version[8];
	hashmap_t hash = NULL;
	hashmap_iter iter = 0;
	char *key, *val;
	int current_body_length=0;
	
	
	buf = http_get_incremental_buffer();
	if (buf == NULL)
	{
		logger_core_error("%s","[http_serialize_request_and_send] sin memoria!");
		return 1;
	}
	RESET_INCREMENTAL_BUFFER(buf);
	
	/** 
	Construyo el request:
	[METHOD] URL[?QUERYSTRING]? HTTP/[VERSION]
	[HEADER]+
	
	[BODY]
	**/
	http_m = *GET_COMMON_FIELD_REF_DIRECT(req, method);
	SET_INCREMENTAL_BUFFER(buf, 
		((http_m == (enum http_method)HTTP_GET)?"GET":"POST"),
		((http_m == (enum http_method)HTTP_GET)?3:4),
			(&ret));
		
	// Agrego URL.
	SET_INCREMENTAL_BUFFER(buf, " ", 1, (&ret));
	ADD_OR_SERIALIZE(buf, request->request_path.buf, request->request_path.len, socket, (&ret), (&sockret), (&sockok));
	
	// Agrego querystring (si el metodo es GET y el querystring tiene valor)
	hash = request->parameters;
	iter = hashmap_first(hash);
	if ((!hashmap_is_end(hash, iter)) && (http_m == (enum http_method)HTTP_GET))
	{
		ADD_OR_SERIALIZE(buf, "?", 1, socket, (&ret), (&sockret), (&sockok));
		ADD_OR_SERIALIZE_HASHMAP(buf, hash, iter, (&key), (&val), "=", 1, "&", 1, socket, (&ret), (&sockret), (&sockok));
	}
	
	ADD_OR_SERIALIZE(buf, " HTTP/", 6, socket, (&ret), (&sockret), (&sockok));
	
	// Agrego version
	sprintf(version, "%d.%d", request->common.http_major, request->common.http_minor);
	ADD_OR_SERIALIZE(buf, version, strlen(version), socket, (&ret), (&sockret), (&sockok));
	ADD_OR_SERIALIZE(buf, "\r\n", 2, socket, (&ret), (&sockret), (&sockok));
	
	// chequeo si se necesito enviar por la red y no se completo la operacion con exito.
	if (sockok == 0)
	{
		http_put_incremental_buffer(buf);
		return 1;
	}
	// Agrego todos los headers.
	hash = http_request_get_headers(request);
	iter = hashmap_first(hash);
	if (!hashmap_is_end(hash, iter))
	{
		ADD_OR_SERIALIZE_HASHMAP(buf, hash, iter, (&key), (&val), ": ", 2, "\r\n", 2, socket, (&ret), (&sockret), (&sockok));
		ADD_OR_SERIALIZE(buf, "\r\n", 2, socket, (&ret), (&sockret), (&sockok));
	}
	ADD_OR_SERIALIZE(buf, "\r\n", 2, socket, (&ret), (&sockret), (&sockok));
	
	if (sockok == 0)
	{
		http_put_incremental_buffer(buf);
		return 1;
	}
	
	// Fuerzo los ultimos elementos para que sean enviados antes de enviar el body
	if (buf->len > 0)
	{
		SEND_ON_SOCKET(socket, buf->buf, buf->len, (&sockret), (&sockok));
	}
	
	// verifico si hay body para enviar
	current_body_length = *GET_COMMON_FIELD_REF_DIRECT(request, current_body_length);
	if (current_body_length > 0)
	{
		SEND_ON_SOCKET(socket, *GET_COMMON_FIELD_REF_DIRECT(request, body), current_body_length, (&sockret), (&sockok));
	}

	http_put_incremental_buffer(buf);
	if (sockok == 0)
	{
		return 1;
	}
	return 0;
}

int http_request_serialize_and_send(http_request_t req)
{
	socket_t socket = http_request_get_socket(req);
	if (socket == NULL)
	{
		return 1;
	}
	return http_request_serialize_and_send_on_socket(req, socket);
}

int http_response_serialize_and_send_on_socket(http_response_t resp, socket_t socket)
{
	incremental_buffer *buf = NULL;
	int ret=0, sockret = 0, sockok = 1;
	http_response *response = (http_response *)resp;
	char static_buf[8];
	hashmap_t hash = NULL;
	hashmap_iter iter = 0;
	char *key, *val;
	int current_body_length=0;
	char *reason=NULL;
	
	buf = http_get_incremental_buffer();

	if (buf == NULL)
	{
		logger_core_error("%s","[http_serialize_response_and_send] sin memoria!");
		return 1;
	}
	RESET_INCREMENTAL_BUFFER(buf);
	
	/** 
	Armo response:
	HTTP/[VERSION] status_code reason_phrase
	[HEADER]+
	
	[BODY]
	**/
	
	// Agrego version
	ADD_OR_SERIALIZE(buf, "HTTP/", 5, socket, (&ret), (&sockret), (&sockok));
	sprintf(static_buf, "%d.%d", response->common.http_major, response->common.http_minor);
	ADD_OR_SERIALIZE(buf, static_buf, strlen(static_buf), socket, (&ret), (&sockret), (&sockok));
	ADD_OR_SERIALIZE(buf, " ", 1, socket, (&ret), (&sockret), (&sockok));
	sprintf(static_buf, "%d", response->common.status_code);
	ADD_OR_SERIALIZE(buf, static_buf, strlen(static_buf), socket, (&ret), (&sockret), (&sockok));
	ADD_OR_SERIALIZE(buf, " ", 1, socket, (&ret), (&sockret), (&sockok));
	
	http_get_reason(response->common.status_code, (const char **)(&reason));
	if (reason != NULL)
	{
		ADD_OR_SERIALIZE(buf, reason, strlen(reason), socket, (&ret), (&sockret), (&sockok));	
	}
	else
	{
		logger_app_warn("[http_serialize_response_and_send] sin descripcion para status_code: %d", response->common.status_code);
	}
	ADD_OR_SERIALIZE(buf, "\r\n", 2, socket, (&ret), (&sockret), (&sockok));
	
	// Agrego todos los headers.
	hash = http_response_get_headers(response);
	iter = hashmap_first(hash);
	if (!hashmap_is_end(hash, iter))
	{
		ADD_OR_SERIALIZE_HASHMAP(buf, hash, iter, (&key), (&val), ": ", 2, "\r\n", 2, socket, (&ret), (&sockret), (&sockok));
		ADD_OR_SERIALIZE(buf, "\r\n", 2, socket, (&ret), (&sockret), (&sockok));
	}
	ADD_OR_SERIALIZE(buf, "\r\n", 2, socket, (&ret), (&sockret), (&sockok));
	
	// chequeo si se necesito enviar por la red y no se completo la operacion con exito.
	if (sockok == 0)
	{
		http_put_incremental_buffer(buf);
		return 1;
	}
	
	// Fuerzo enviar los ultimos elementos de buf.
	if (buf->len > 0)
	{
		SEND_ON_SOCKET(socket, buf->buf, buf->len, (&sockret), (&sockok));
	}
	
	// verifico si hay body.
	current_body_length = *GET_COMMON_FIELD_REF_DIRECT(response, current_body_length);
	if (current_body_length > 0)
	{
		SEND_ON_SOCKET(socket, *GET_COMMON_FIELD_REF_DIRECT(response, body), current_body_length, (&sockret), (&sockok));
	}

	http_put_incremental_buffer(buf);
	if (sockok == 0)
	{
		return 1;
	}
	return 0;
}

int http_response_serialize_and_send(http_response_t resp)
{
	socket_t socket = http_response_get_socket(resp);
	if (socket == NULL)
	{
		return 1;
	}
	return http_response_serialize_and_send_on_socket(resp, socket);
}

